package srzy.diameter

import srzy.common.packunpack

object NodeToBytes {
  import srzy.diameter.DataFormat._

  def addAVPHead(name: String, arr: Array[Byte]): Array[Byte] = {
    val avpInfo = DataFormat.avpDict.find(x => x._1 == name).get
    val avpName = avpInfo._1
    val avpCode = avpInfo._2
    val avpType = avpInfo._3
    val avpFields = avpInfo._4.map(x => (x._1, x._2.toLong))
    val optionFlag = avpFields.exists(x => x._1 == option._1)

    val body = padBytes(arr)

    val header = if (optionFlag == false) {
      Array.fill(noOptionAVPHeaderLength)(0.asInstanceOf[Byte])
    } else {
      Array.fill(withOptionAVPHeaderLength)(0.asInstanceOf[Byte])
    }
    val length: Long = header.length + arr.length

    (avpFields ::: (DataFormat.avpHeaderLengthText, length) :: Nil).foreach(x => {
      val avpFieldName = x._1
      val avpFieldValue = x._2
      val offset = withOptionAVPHeaderFieldsInfoMap.apply(x._1)._1
      val digit = withOptionAVPHeaderFieldsInfoMap.apply(x._1)._2
      packunpack.packLong(header, offset, avpFieldValue, digit)
    })
    header ++ body
  }

  def addDCCHead(name: String, body: Array[Byte], id: List[(String, Long)] = List.empty[(String, Long)]): Array[Byte] = {
    val dccInfo = dccHeaderDict.find(x => x._1 == name).get
    val dccName = dccInfo._1
    val dccFields = dccInfo._2.map(x => (x._1, x._2.toLong))

    val header = Array.fill(dccHeaderLength)(0.asInstanceOf[Byte])

    val length: Long = header.length + body.length

    ((dccFields ++ id) ::: (DataFormat.dccHeaderLengthText, length) :: Nil).foreach(x => {
      val avpFieldName = x._1
      val avpFieldValue = x._2
      val offset = dccHeaderFieldsInfoMap.apply(x._1)._1
      val digit = dccHeaderFieldsInfoMap.apply(x._1)._2
      packunpack.packLong(header, offset, avpFieldValue, digit)
    })
    header ++ body
  }

  def dccToBytes(node: scala.xml.Node, id: List[(String, Long)] = List.empty[(String, Long)]): Array[Byte] = {
    if (node.label != dccHeaderName) {
      throw new Exception("DCC node label :" + node.label + " error")
    }
    val dccType = node.attributes.apply("name").toString
    val body = avpToBytes(node.nonEmptyChildren.filter(_.label == avpHeaderName).toList)
    addDCCHead(dccType, body, id)
  }

  def avpToBytes(node: scala.xml.Node): Array[Byte] = {
    if (node.label != DataFormat.avpHeaderName) {
      throw new Exception("AVP node label :" + node.label + " error")
    }
    val name = node.attributes.get("name").getOrElse("").toString
    val value = node.attributes.get("value").getOrElse("").toString
    val avpInfo = DataFormat.avpDict.find(x => x._1 == name) match {
      case Some(v) => v
      case None => throw new Exception("can not find avpInfo " + name)
    }
    val avpType = avpInfo._3
    val r = "0[xX]([0-9A-Za-z]+)".r
    val arr = avpType match {
      case "Long" => packunpack.packLong(Array.fill(8)(0.asInstanceOf[Byte]), 0, if (value == "") 0 else value.toLong)
      case "Int" => packunpack.packInt(Array.fill(4)(0.asInstanceOf[Byte]), 0, if (value == "") 0 else value.toInt)
      case "UnsignedInt" =>
        packunpack.packLong(Array.fill(8)(0.asInstanceOf[Byte]), 0, if (value == "") 0 else value.toLong).drop(4)
      case "UTF8String" => value.getBytes("UTF-8")
      case "Grouped" => {
        avpToBytes(node.nonEmptyChildren.filter(x => x.label == DataFormat.avpHeaderName).toList)
        //        node.nonEmptyChildren.filter(x => x.label == DataFormat.avpHeaderName).toList.map(x => {
        //          avpToBytes(x)
        //        }).reduceLeft(_ ++ _)
      }
      case "String" => {
        r.findFirstMatchIn(value) match {
          case Some(v) => {
            packunpack.hexStr2Bytes(v.group(1))
          }
          case None => value.getBytes("ISO-8859-1")
        }
      }
    }
    addAVPHead(name, arr)
  }

  def avpToBytes(nodes: List[scala.xml.Node]): Array[Byte] = {
    nodes.map(avpToBytes(_)).foldLeft(Array.empty[Byte])(_ ++ _)
  }

}
